/*
 * Copyright (C) 2011 Drogolov Roman Olegovich <drull@drull.org.ru>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, see <http://www.gnu.org/licenses/>
 *
 */

#include <QApplication>
#include <QBitmap>
#include <QPixmap>
#include <QSignalMapper>
#include <QPushButton>
#include <QTimer>

#include "notifywidget.h"
#include "dbus.h"
#include "qclicklabel.h"
#include "notifyarea.h"
#include "message.h"

#define FADE_TIMER_TIMEOUT 20

NotifyWidget::NotifyWidget(std::vector<Message*> &msgStack, NotifyArea *p, bool debug)
    : parent(p),
      messageStack(msgStack),
      fadeTimer(new QTimer(this)),
      timer(new QTimer(this)),
      text(new QClickLabel(this)),
      icon(new QClickLabel(this)),
      signalMap(new QSignalMapper(this)),
      maxOpacity(0.7),
      widgetStyle(),
      debugMode(debug)
{
    icon->setScaledContents(true);
    for (int i = 0; i < 10; i++)
    {
        actionButton[i] = new QPushButton(this);
        actionButton[i]->setVisible(false);
    }

    signalMap->setMapping(timer, 1);
    signalMap->setMapping(text, 2);
    signalMap->setMapping(icon, 2);

    connect(fadeTimer, SIGNAL(timeout()), this, SLOT(fadeWidget()));

    connect(timer, SIGNAL(timeout()), signalMap, SLOT(map()));
    connect(text,  SIGNAL(clicked()), signalMap, SLOT(map()));
    connect(icon,  SIGNAL(clicked()), signalMap, SLOT(map()));

    connect(signalMap, SIGNAL(mapped(int)),     this, SLOT(hideWidget(int)));
    connect(signalMap, SIGNAL(mapped(QString)), this, SLOT(emitActionInvoked(QString)));

    setWindowFlags(Qt::SplashScreen | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
    setAttribute(Qt::WA_X11NetWmWindowTypeNotification);
    text->setAttribute(Qt::WA_NoSystemBackground);
    icon->setAttribute(Qt::WA_NoSystemBackground);
}
///////////////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////////////


void NotifyWidget::fadeWidget()
{
    if (windowOpacity() > 0 && isVisible())
    {
        setWindowOpacity(windowOpacity() - maxOpacity / 10);
    }
    else
    {
        setVisible(false);
        fadeTimer->stop();
    }
}

///////////////////////////////////////////////////////////////////////////////////////

void NotifyWidget::appendMsg() // WTF???
{
    if (debugMode) fprintf(stderr, "Appending messages\n");

    for (std::vector<Message*>::iterator iter = messageStack.begin(); iter != messageStack.end(); ++iter) //Check if need to append
    {
        for (std::vector<Message*>::iterator iter2 = iter; iter2 != messageStack.end(); ++iter2)
        {
            if ((*iter)->appName == (*iter2)->appName && 
                (*iter)->header == (*iter2)->header && 
                !(*iter2)->isComplete && 
                iter != iter2)
            {
                if ((*iter2)->text.size() > 0)
                    (*iter)->text += QString("<br>") + (*iter2)->text;
                (*iter)->isComplete = false;
                (*iter2)->isComplete = true;
            }
        }
    }
    for (std::vector<Message*>::iterator iter = messageStack.begin(); iter != messageStack.end(); ) //Erase messages, that are already appended
    {
        if ((*iter)->isComplete && iter != messageStack.begin())
        {
            delete *iter;
            iter = messageStack.erase(iter);
        }
        else
        {
            iter++;
        }
    }

    if (debugMode) fprintf(stderr, "Appending complete\n");
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void NotifyWidget::hideWidget(int reason)
{
    if (debugMode) fprintf(stderr, "Attempt to hide widget\n");

    timer->stop();

    if (reason == 2)
        emit ActionInvoked(messageStack.front()->id, "default");

    if (debugMode) fprintf(stderr, "Erasing first notification is stack\n");

    delete messageStack.front();
    for (int i = 0; i < 10; i++)
    {
        signalMap->removeMappings(actionButton[i]);
        if(actionButton[i]->isVisible())
        {
            actionButton[i]->setVisible(false);
        }
    }

    messageStack.erase(messageStack.begin());
    if (messageStack.size() > 0)
    {
        showWidget();
    }
    else
    {
        if (debugMode) fprintf(stderr, "Starting fade.\n");
        fadeTimer->start(FADE_TIMER_TIMEOUT);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
void NotifyWidget::showWidget()
{
    if (debugMode) fprintf(stderr, "Attempt to show new notification\n");

    QString myText;
    if (messageStack.size() > 0)
    {
        setStyleSheet(widgetStyle);
        setWindowOpacity(maxOpacity);

        // Set icon
        icon->setPixmap(messageStack.front()->icon);
        icon->resize(std::min(maxIconSize, messageStack.front()->icon.width()),
                     std::min(maxIconSize, messageStack.front()->icon.height()));

        //-----------------------------------------------------------------------------
        myText += "<html><table border=0><tr>"; //Create table on label
        myText += "<td VALIGN=middle><div>";
        myText += parent->getUrgencyTag(messageStack.front()->urgency);
        myText += messageStack.front()->header + QString("</div>");

        if (messageStack.front()->text.size() > 0)
            myText += messageStack.front()->text;

        myText += "</td></tr></table>"; // End of table
        //-----------------------------------------------------------------------------

        text->setText(myText);	// Move items
        text->adjustSize();
        if (text->height() < icon->height())
            text->resize(text->width(), icon->height());
        if (text->width() < 190)
            text->resize(190, text->height());
        if (text->height() < 60)
            text->resize(text->width(), 60);


        icon->move(10, 5);
        text->move(icon->width() + icon->pos().x() + 10, 5);

        int x = text->pos().x();
        int y = text->pos().y() + text->height() + 5;

        for (int i = 0; i < messageStack.front()->action.size() && i < 10; i += 2) //Construct action buttons. Each even element is action key, each odd element is action text.
        {
            actionButton[i / 2]->move(x, y);
            actionButton[i / 2]->setText(messageStack.front()->action.at(i + 1));

            actionButton[i / 2]->resize(std::max(text->width(), 100), actionButton[i / 2]->height());
            connect(actionButton[i / 2], SIGNAL(clicked()), signalMap, SLOT(map()));
            signalMap->setMapping(actionButton[i / 2], messageStack.front()->action.at(i));
            y += actionButton[i / 2]->height() + 5;
            actionButton[i / 2]->setVisible(true);
        }

        resize(childrenRect().size().width() + 20, childrenRect().size().height() + 10);

        QPixmap pixmap(size()); // Cutting QWidget corners
        pixmap.fill(Qt::transparent);
        render(&pixmap, QPoint(), QRegion(), DrawChildren | IgnoreMask);
        setMask(pixmap.mask());
        
        x = position.x() - size().width() - 10;
        y = position.y() - size().height() - 10;
        if ( x < 0)
            x = 0;
        if ( y < 0)
            y = 0;
        move(x, y);

        messageStack.front()->isComplete = true;

        setVisible(true);
        raise();
        timer->start(messageStack.front()->timeout);
    }			

}

/////////////////////////////////////////////////////////////////////////////////////
void NotifyWidget::checkIfNeedToShow()
{
if (debugMode) fprintf(stderr, "Checking if need to show new notification\n");

if (messageStack.size() > 1 && 
    (messageStack.front()->hints["x-canonical-append"].toString() == "allow" || 
     messageStack.front()->hints["append"].toString() == "allow"))
    appendMsg();
if (!messageStack.empty() && (!timer->isActive() || !messageStack.front()->isComplete))
    showWidget();
}

//////////////////////////////////////////////
void NotifyWidget::emitActionInvoked(QString actionId)
{
    emit ActionInvoked(messageStack.front()->id, actionId);
    hideWidget(4);
}
